Skip to content

feat: Tab/Shift+Tab navigation and F2 inline edit for label list#262

Open
phobo3s wants to merge 16 commits into
PFCCLab:mainfrom
phobo3s:feat/keyboard-navigation
Open

feat: Tab/Shift+Tab navigation and F2 inline edit for label list#262
phobo3s wants to merge 16 commits into
PFCCLab:mainfrom
phobo3s:feat/keyboard-navigation

Conversation

@phobo3s

@phobo3s phobo3s commented Jun 4, 2026

Copy link
Copy Markdown

Summary

Adds keyboard shortcuts for navigating and editing OCR detection results without mouse clicks.

Shortcuts added

Key Action
Tab Next detection box
Shift+Tab Previous detection box
F2 Open inline text editor for focused box
Enter Confirm edit and move to next box

Motivation

When correcting OCR output on a dense document (50–200 boxes), the current workflow requires a mouse click for every single box. With these shortcuts, the entire correction pass can be done keyboard-only — Tab to navigate, F2 to edit, Enter to confirm and continue.

This pattern is standard in annotation tools (LabelImg, Label Studio, CVAT).

Changes

  • PPOCRLabel.py: Added QShortcut bindings for Tab/Shift+Tab/F2, added _navigateLabel() method
  • libs/editinlist.py: Added activate_edit() method, Enter in edit mode now moves to next item

closes #261

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds keyboard-first navigation and inline editing for OCR detection results to reduce mouse usage during correction workflows.

Changes:

  • Adds Tab / Shift+Tab navigation across label list items, and F2 (plus Enter/Return handling) to trigger inline editing.
  • Introduces _navigateLabel() to programmatically advance selection and keep the selected item visible/focused.
  • Extends EditInList with activate_edit() and updates Enter behavior in edit mode to advance to the next item.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
PPOCRLabel.py Wires new shortcuts/keypress handlers and adds _navigateLabel() for label navigation + focus behavior.
libs/editinlist.py Adds activate_edit() and modifies Enter handling to close the editor, advance selection, and re-open editing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread PPOCRLabel.py Outdated
import signal
import subprocess
import sys
import torch
Comment thread PPOCRLabel.py Outdated
Comment on lines +1365 to +1366
self.focusAndZoom() # ← Ctrl+G'nin yaptığı
self.labelList.activate_edit() # ← F2'nin yaptığı
Comment thread libs/editinlist.py Outdated
Comment on lines +43 to +53
def keyPressEvent(self, event) -> None:
# close edit
if event.key() in [16777220, 16777221]:
if event.key() in [16777220, 16777221]: # Enter / Return
for i in range(self.count()):
self.closePersistentEditor(self.item(i))
# bir sonraki satıra geç ve editörü aç
next_row = self.currentRow() + 1
if next_row < self.count():
self.setCurrentRow(next_row)
self.scrollToItem(self.item(next_row))
self.activate_edit() No newline at end of file
Comment thread PPOCRLabel.py Outdated
Comment on lines +1362 to +1364
self.labelList.setCurrentRow(next_row)
self.labelList.scrollToItem(self.labelList.item(next_row))
self.labelSelectionChanged()
phobo3s and others added 14 commits June 4, 2026 16:15
- Remove unused `import torch` (caused ImportError for users without PyTorch)
- Remove redundant `labelSelectionChanged()` call in `_navigateLabel()` since
  setCurrentRow() already triggers the connected itemSelectionChanged signal
- Remove auto `activate_edit()` from `_navigateLabel()`: Tab navigates only,
  F2 opens editor (matches PR description)
- Fix `EditInList.keyPressEvent`: call `event.accept()` + return on Enter,
  fall through to `super().keyPressEvent()` for all other keys so arrow keys
  and normal list navigation keep working

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
- Tab: close editor (save), move to next item, open editor there
- Shift+Tab: close editor (save), move to previous item, open editor there
- Enter/Return: close editor (save), move to next item (no auto-edit)
- Extract _close_editors() helper to avoid repetition

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
QLineEdit (the persistent editor widget) was consuming Tab for focus
traversal before EditInList.keyPressEvent ever saw it. Install an
eventFilter on the editor widget in activate_edit() so Tab/Shift+Tab
are intercepted at the source, closing the editor (saving the value)
and advancing to the next/previous item with a fresh editor open.

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
closePersistentEditor does not flush the QLineEdit value back to the
model (it closes with NoHint). Call commitData(editor) first so the
typed text is actually saved before navigation happens.

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
Root cause: Qt fires QShortcut BEFORE the key event reaches the focused
widget. The Tab QShortcut was navigating without saving, and the
eventFilter on the QLineEdit was never called (event consumed by shortcut).

Fix:
- Remove Tab/Shift+Tab QShortcuts and keyPressEvent handlers from MainWindow
- In EditInList.eventFilter: intercept Tab/Shift+Tab from the QLineEdit,
  call commitData() to flush value to model, then navigate and reopen editor
- In EditInList.keyPressEvent: handle Tab when no editor is open (plain nav)
- Rename _close_editors to _commit_and_close to make intent clear

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
Previous attempts failed because editItem() and openPersistentEditor()
create two separate QLineEdit widgets; the eventFilter was installed on
the wrong one. Also commitData() on a persistent editor does not flush
the typed text.

New approach:
- Install a QApplication-level eventFilter when editing starts (catches
  Tab from whichever QLineEdit child actually has focus)
- Save by finding the visible QLineEdit child and calling item.setText()
  directly — bypasses all delegate machinery, guaranteed to work
- Remove the app filter when editing ends to avoid side effects

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
Adds a dock widget with a slider (0–1.00) on the right panel.
Boxes with OCR confidence >= threshold are hidden; only those below
the threshold are drawn. Default = 1.00 shows everything.

Implementation:
- Shape.score attribute stores OCR rec_score (None if not from OCR)
- After autoRecognitionCurrent, scores are copied from result_dic to shapes
- applyConfidenceFilter() calls canvas.setShapeVisible() per shape
- Slider re-runs the filter on every change

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
Removing torch caused c10.dll errors on Windows when PaddlePaddle
is installed alongside PyTorch. Keep the import with a noqa marker
so flake8 doesn't flag it as unused.

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
Score is saved as an optional 'score' field in Label.txt entries
(backwards compatible — old files without it load fine, score=None).

Changes:
- saveLabels: write score from result_dic and from shape.score
- loadLabels: read score from tuple item[4], set shape.score
- showBoundingBoxFromPPlabel: pass score through tuples, call filter after load
- locked shapes pass None score (no OCR confidence available)

Slider now filters correctly both after OCR and after re-opening saved files.

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
When a box is selected, the Confidence Filter dock label updates to
show both the current threshold and the selected shape's score:
"Threshold: 0.87  |  Selected: 0.73"

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
findChildren(QLineEdit) was finding the wrong widget. The focused widget
IS the QLineEdit the user is typing in, so use that directly.
Falls back to findChildren only if focusWidget is not a QLineEdit.

https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
Wire EditInList.navigated signal to MainWindow.focusAndZoom so that
pressing Tab/Shift+Tab while editing labels also triggers the same
canvas centering and zoom that Ctrl+G provides.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
Add pyqtSignal import and navigated signal declaration, then emit it
after every Tab/Shift+Tab move in both eventFilter (editor open) and
keyPressEvent (no editor open).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSG7xRPGp19iVAs9UPDPPb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Keyboard navigation between detection boxes (Tab) and inline text edit (F2 / Enter)

3 participants